"""
:synopsis: Generate Fishbone Analysis
:authors: Riley Baird (OK), Emma Baker (OK)
"""


# lib import
from ...lib.session import config
import sys
import os
import random
import datetime
import arcpy
import pandas as pd
# from arcpy import (XYToLine_management, CreateAddressLocator_geocoding, GeocodeAddresses_geocoding, GetParameterAsText,
#                    MakeFeatureLayer_management, FeatureClassToFeatureClass_conversion, Intersect_analysis, AddField_management,
#                    CalculateField_management, DeleteField_management, Project_management, AddGeometryAttributes_management, SetProgressor,
#                    SetProgressorPosition, ResetProgressor, SetProgressorLabel)
from arcgis.features import GeoAccessor, GeoSeriesAccessor
try:
    from typing import ClassVar, Optional, Union, Protocol, TypeVar, Generic, TypeVar, overload, get_args, Literal
except:
    pass


random_start_phrase = random.choice(['Make it so!', 'Hello World!', 'As the prophecy foretold...', 'Greetings earthlings.', 'All are bases are belong to us.', 'The Jig is up!'])

required_dataset_name = config.gdb_info.required_dataset_name # "NG911"
optional_dataset_name = config.gdb_info.optional_dataset_name # "OptionalLayers"
tool_switch: str = "Generate_Fishbone_Analysis"


error_list = ["PLEASE SELECT A FEATURE CLASS", "NO FIELDS FOUND IN SELECTED FC", "--PLEASE SELECT FIELD--"]


## Custom Tool Parameter Information
class CustomToolParameterInfo:
    def __init__(self):
        ## Parameter Indexes
        self.output_folder_idx = 0
        self.std_ap_fc_idx = self.output_folder_idx + 1
        self.ap_field_table_parameter_idx = self.std_ap_fc_idx + 1
        self.std_road_fc_idx = self.ap_field_table_parameter_idx + 1
        self.road_field_table_parameter_idx = self.std_road_fc_idx + 1
        ## Std Field Names
        # Address Point Parameters
        self.address_fc = config.feature_classes.address_point
        self.ap_unique_id_field = self.address_fc.unique_id
        self.ap_fulladdr_field = config.fields.fulladdr
        self.ap_city_field = config.fields.city
        self.ap_state_field = config.fields.state
        self.ap_zipcode_field = config.fields.zipcode
        # Road Centerline Parameters
        self.road_fc = config.feature_classes.road_centerline
        self.road_unique_id_field = self.road_fc.unique_id
        self.road_fullname_field = config.fields.fullname
        self.road_add_l_from_field = config.fields.add_l_from
        self.road_add_l_to_field = config.fields.add_l_to
        self.road_add_r_from_field = config.fields.add_r_from
        self.road_add_r_to_field = config.fields.add_r_to
        self.road_parity_l_field = config.fields.parity_l
        self.road_parity_r_field = config.fields.parity_r
        self.road_premod_field = config.fields.premod
        self.road_predir_field = config.fields.predir
        self.road_pretype_field = config.fields.pretype
        self.road_pretypesep_field = config.fields.pretypesep
        self.road_street_field = config.fields.street
        self.road_streettype_field = config.fields.streettype
        self.road_sufdir_field = config.fields.sufdir
        self.road_sufmod_field = config.fields.sufmod
        self.road_city_l_field = config.fields.city_l
        self.road_city_r_field = config.fields.city_r
        self.road_county_l_field = config.fields.county_l
        self.road_county_r_field = config.fields.county_r
        self.road_country_l_field = config.fields.country_l
        self.road_country_r_field = config.fields.country_r
        self.road_state_l_field = config.fields.state_l
        self.road_state_r_field = config.fields.state_r
        self.road_zipcode_l_field = config.fields.zipcode_l
        self.road_zipcode_r_field = config.fields.zipcode_r
        ## Parameter Lists
        self.street_fields = [
            self.road_premod_field.name,
            self.road_predir_field.name,
            self.road_pretype_field.name,
            self.road_pretypesep_field.name,
            self.road_street_field.name,
            self.road_streettype_field.name,
            self.road_sufdir_field.name,
            self.road_sufmod_field.name
        ]
        # self.ap_field_list_for_analysis = [
        #     self.ap_unique_id_field,
        #     self.ap_fulladdr_field.name,
        #     self.ap_city_field.name,
        #     self.ap_state_field.name,
        #     self.ap_zipcode_field.name
        # ]
        ## Road Analysis Dict for Tool
        self.road_analysis_category_list = {
            'Feature ID':self.road_unique_id_field.name,
            '*From Left':self.road_add_l_from_field.name,
            '*To Left':self.road_add_l_to_field.name,
            '*From Right':self.road_add_r_from_field.name,
            'Right Parity':self.road_parity_l_field.name,
            'Full Street Name':self.road_fullname_field.name,
            'Prefix Direction':self.road_predir_field.name,
            'Prefix Type':self.road_pretype_field.name,
            '*Street Name':self.road_street_field.name,
            'Suffix Type':self.road_streettype_field.name,
            'Suffix Direction':self.road_sufdir_field.name,
            'Left City or Place':self.road_city_l_field.name,
            'Right City or Place':self.road_city_r_field.name,
            'Left County':self.road_county_l_field.name,
            'Right County':self.road_county_r_field.name,
            'Left State':self.road_state_l_field.name,
            'Right State':self.road_state_r_field.name,
            'Left State Abbreviation':self.road_state_l_field.name,
            'Right State Abbreviation':self.road_state_r_field.name,
            'Left ZIP Code':self.road_zipcode_l_field.name,
            'Right ZIP Code':self.road_zipcode_r_field.name
        }
        # ap_field_string = f"'Street or Intersection' {list(ap_field_dict.values())[1]} VISIBLE NONE;'City or Placename' {list(ap_field_dict.values())[2]} VISIBLE NONE;State {list(ap_field_dict.values())[3]} VISIBLE NONE;'ZIP Code' {list(ap_field_dict.values())[4]} VISIBLE NONE"  # TODO: Fix - I commented this out because it was crashing during debugging elsewhere (-Riley, 2/27/25)
        self.locator_style = "US Address - Dual Ranges"
        self.primary_reference_data = "Street_Centerline StreetAddress"
custom_param = CustomToolParameterInfo

class GenerateFishboneAnalysis:
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Generate Fishbone Analysis"
        self.description = "Generate Fishbone Analysis"
        self.canRunInBackground = False
        self.category = "3 - Enhancement"

    def getParameterInfo(self):
        """Define parameter definitions"""

        params = []

        output_folder = arcpy.Parameter(
            displayName=f"Output Folder Location for Fishbone Analysis",
            name="output_folder",
            datatype="DEFolder",
            parameterType="Required",
            direction="Input")
        params += [output_folder]

        std_gdb = arcpy.Parameter(
            displayName=f"Select standard geodatabase.\n - Requires standard schema, {custom_param().address_fc.name}, and {custom_param().road_fc.name}.",
            name="std_gdb",
            datatype="DEWorkspace",
            parameterType="Required",
            direction="Input")
        params += [std_gdb]

        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        ## table parameter
        return


    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        ## table parameter
        # fc_std_path = parameters[fc_std_name_idx]
        # cur_fc_fields = [fc.fields for fc in config.feature_classes.values() if fc.name == fc_std_name.value][0]
        # std_nguid_field_ns = [field_ns for field_ns in cur_fc_fields.values() if 'nguid' in field_ns.name.lower()][0]
        # std_nguid_field_name = std_nguid_field_ns.name
        # fc_path = parameters[fc_path_idx]
        # assign_method = parameters[assign_method_idx]
        # format_switch = parameters[old_to_new_format_idx]
        # delete_old_field = parameters[delete_old_field_idx]
        # keep_field_name = parameters[keep_field_name_idx]
        # field_table = parameters[field_table_idx]
        # user_nguid_field = field_table.values[0][user_nguid_field_idx]
        # user_local_field = field_table.values[0][user_local_field_idx]
        # user_agency_field = field_table.values[0][user_agency_field_idx]
        # if not fc_path.value and fc_path.hasBeenValidated:
        #     fc_path.setErrorMessage(f"{error_list[0]}")
        # if format_switch.value and user_nguid_field in error_list:
        #     format_switch.setErrorMessage(f"To switch from the old format (`@`) to the new format (`:`), please specified `NGUID` field")
        # # if delete_old_field.value and user_nguid_field in error_list:
        # #     delete_old_field.setWarningMessage(f"Delete Old `NGUID` has been selected, but no `NGUID` was specified.")
        # if not delete_old_field.value:
        #     keep_field_name_value = keep_field_name.value
        #     if fc_path.value and fc_path.hasBeenValidated:
        #         fc_field_list = [field.name.lower() for field in arcpy.Describe(fc_path.valueAsText).fields]
        #         if keep_field_name_value.lower() in fc_field_list:
        #             delete_old_field.setErrorMessage(f"Selected Field Name for old `NGUID` already exists in feature class. Please select a different field name string.")
        # if assign_method.value == method_option_list[0] and user_nguid_field in error_list:
        #     assign_method.setErrorMessage(f"With assign method set to `{assign_method.value}`, please specified `NGUID` field")
        # if assign_method.value == method_option_list[1] and user_local_field in error_list:
        #     assign_method.setErrorMessage(f"With assign method set to `{assign_method.value}`, please specified `{local_std_field_name}` field")
        # if assign_method.value != method_option_list[0] and user_agency_field in error_list:
        #     field_table.setWarningMessage(f"No `{agency_std_field_name}` field specified. `NGUID` will be assigned using a blank sting for `Agency` part.")

        return


    def execute(self, parameters, messages):
        """The source code of the tool."""
        ## Process parameters from user-specified values
        output_folder_path = parameters[custom_param().output_folder_idx].valueAsText
        std_gdb_path = parameters[custom_param().std_ap_fc_idx].valueAsText
        address_fc_path = os.path.join(std_gdb_path, config.gdb_info.required_dataset_name, custom_param().address_fc.name)
        road_fc_path = os.path.join(std_gdb_path, config.gdb_info.required_dataset_name, custom_param().road_fc.name)

        rcl_path_argument = f"'{custom_param().road_fc.name}' 'Primary Table'"
        ap_field_string = f"'Street or Intersection' {list(ap_field_dict.values())[1]} VISIBLE NONE;'City or Placename' {list(ap_field_dict.values())[2]} VISIBLE NONE;State {list(ap_field_dict.values())[3]} VISIBLE NONE;'ZIP Code' {list(ap_field_dict.values())[4]} VISIBLE NONE"
        road_field_string_list = []
        for key, road_cat_str in enumerate(custom_param().road_analysis_category_list):
            if road_cat_str in ['Left State Abbreviation', 'Right State Abbreviation']:
                current_rcld_field = "''"
            else:
                road_key = key
                if key >= (len(custom_param().road_analysis_category_list) - 3):
                    road_key = road_key - 2
                current_rcld_field = list(road_field_dict.values())[road_key]
            current_string = f"'{road_cat_str}' {current_rcld_field} VISIBLE NONE"
            road_field_string_list.append(current_string)
        road_field_string = ";".join(road_field_string_list)

        arcpy.AddMessage(f"{random_start_phrase}")
        arcpy.AddMessage(f"\nBeginning Fishbone Analysis...")

        arcpy.AddMessage(f"\n\tCreating Address Locator...")
        locator_file = os.path.join(output_folder_path, "Locator")
        arcpy.geocoding.CreateLocator(custom_param().locator_style, rcl_path_argument, road_field_string, locator_file, config_keyword="", enable_suggestions="DISABLED")

        arcpy.AddMessage(f"\n\tPreparing for Address Geocoding for {os.path.basename(std_ap_fc_path)}...")


        arcpy.AddMessage(f"\tTEXT.")

        return


if __name__ == "__main__":
    raise Exception("This module is a dependency of an ArcGIS Python Toolbox and should not be executed directly.")